package fschmidt.util.java;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;


public final class ClassUtils {

	private ClassUtils() {}  // never

	private static final class Key {
		private final String name;
		private final Class[] parameterTypes;

		private Key(Method m) {
			this(m.getName(),m.getParameterTypes());
		}

		private Key(String name,Class[] parameterTypes) {
			this.name = name;
			this.parameterTypes = parameterTypes;
		}

		public boolean equals(Object obj) {
			if( !(obj instanceof Key) )
				return false;
			Key key = (Key)obj;
			return key.name.equals(name) && Arrays.equals(key.parameterTypes,parameterTypes);
		}

		public int hashCode() {
			return name.hashCode()+31*Arrays.hashCode(parameterTypes);
		}
	}

	private static final Map<Class,FutureValue<Map<Key,Method>>> cache = new WeakHashMap<Class,FutureValue<Map<Key,Method>>>();

	// return null if not found
	public static Method getMethod(final Class cls,String name,Class... parameterTypes) {
		FutureValue<Map<Key,Method>> ft;
		synchronized(cache) {
			ft = cache.get(cls);
			if( ft==null ) {
				ft = new FutureValue<Map<Key,Method>>() {
					protected Map<Key,Method> compute() {
						Map<Key,Method> map = new HashMap<Key,Method>();
						for( Method m : cls.getMethods() ) {
							Method m2 = map.put(new Key(m),m);
							if( m2 != null )
								throw new RuntimeException("duplicate methods "+m+" and "+m2);
						}
						return map;
					}
				};
				cache.put(cls,ft);
			}
		}
		Key key = new Key(name,parameterTypes);
		return ft.get().get(key);
	}

}
